package com.wsc.controller;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.protocol.BufferingHttpClientHandler;
import org.apache.http.nio.protocol.EventListener;
import org.apache.http.nio.protocol.HttpRequestExecutionHandler;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.SessionRequest;
import org.apache.http.nio.reactor.SessionRequestCallback;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;

public class ControllerClient {
    String resonpharse,protocolversion;
    int statuscode;
    
    public void setresponsedetails(String rpharse,String rversion,int rcode){
        resonpharse = rpharse;
        protocolversion = rversion;
        statuscode = rcode;
    }
    
        
    public int getStatuscode() {
		return statuscode;
	}


	public void setStatuscode(int statuscode) {
		this.statuscode = statuscode;
	}


	public void sendRequest(String hostname,int port,final String requesttype) throws Exception {
        //creating a logfilename to record this class actions
        Date dt = new Date();
        DateFormat df = new SimpleDateFormat("yyyy-mm-dd");
        String ControllerClientlogfile = "ControllerClient"+df.format(dt);
        Logger.writeLog(ControllerClientlogfile, "Sending Request details\n");
        Logger.writeLog(ControllerClientlogfile, "Host Name :"+hostname+"\n Port Num :"+port+"\n Request Type :"+requesttype+"\n");
        
        HttpParams params = new SyncBasicHttpParams();
        params
            .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
            .setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000)
            .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
            .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
            .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
            .setParameter(CoreProtocolPNames.USER_AGENT, "HttpComponents/1.1");
        
        /*IOReactorConfig ioconfig = new IOReactorConfig();
        ioconfig.setConnectTimeout(10000);
        ioconfig.setSoTimeout(5000);
        ioconfig.setIoThreadCount(2);
        ioconfig.setTcpNoDelay(true);*/
        final ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(1,params);

        HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
                new RequestContent(),
                new RequestTargetHost(),
                new RequestConnControl(),
                new RequestUserAgent(),
                new RequestExpectContinue()});
        
        // We are going to use this object to synchronize between the 
        // I/O event and main threads
        CountDownLatch requestCount = new CountDownLatch(1);
        
        BufferingHttpClientHandler handler = new BufferingHttpClientHandler(
                httpproc,
                new MyHttpRequestExecutionHandler(requestCount,requesttype,this),
                new DefaultConnectionReuseStrategy(),
                params);

        handler.setEventListener(new EventLogger());
        
        final IOEventDispatch ioEventDispatch = new DefaultClientIOEventDispatch(handler, params);
        
        Thread t = new Thread(new Runnable() {
         
            @Override
            public void run() {
                try {
                    ioReactor.execute(ioEventDispatch);
                } catch (InterruptedIOException ex) {
                    System.err.println("Interrupted");
                } catch (IOException e) {
                    System.err.println("I/O error: " + e.getMessage());
                }
                System.out.println("Shutdown");
            }
            
        });
        t.start();

        SessionRequest reqs = ioReactor.connect(
                new InetSocketAddress(hostname, port), 
                null, 
                new HttpHost(hostname),
                new MySessionRequestCallback(requestCount));
       
        // Block until all connections signal
        // completion of the request execution
        requestCount.await();
        
        System.out.println("Shutting down I/O reactor");
        
        ioReactor.shutdown();
        
        System.out.println("Done");
       
    }
    
    static class MyHttpRequestExecutionHandler implements HttpRequestExecutionHandler {

        private final static String REQUEST_SENT       = "request-sent";
        private final static String RESPONSE_RECEIVED  = "response-received";
        String requesttype;
        ControllerClient ct;
        
        private final CountDownLatch requestCount;
        
        public MyHttpRequestExecutionHandler(final CountDownLatch requestCount,String reqtype,ControllerClient cc) {
            super();
            this.requestCount = requestCount;
            requesttype = reqtype;
            ct = cc;
       }
        
        @Override
        public void initalizeContext(final HttpContext context, final Object attachment) {
            HttpHost targetHost = (HttpHost) attachment;
            context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);
        }
        
        @Override
        public void finalizeContext(final HttpContext context) {
            Object flag = context.getAttribute(RESPONSE_RECEIVED);
            if (flag == null) {
                // Signal completion of the request execution
                requestCount.countDown();
                //System.out.println("flag is null");
            }
        }

        @Override
        public HttpRequest submitRequest(final HttpContext context) {
            //Logger.writeLog(ControllerClientlogfile, "logfile creation is done");
            System.out.println("request submission started");
            HttpHost targetHost = (HttpHost) context.getAttribute(
                    ExecutionContext.HTTP_TARGET_HOST);
            Object flag = context.getAttribute(REQUEST_SENT);
            if (flag == null) {
                // Stick some object into the context
                context.setAttribute(REQUEST_SENT, Boolean.TRUE);

                System.out.println("--------------");
                System.out.println("Sending request to " + targetHost);
                System.out.println("--------------");
                
               // return new BasicHttpRequest("GET", "/index.html");
                
                BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/index.html");
//                request.g
            request.setHeader("reqtype",requesttype);
            
            System.out.println("request type set in header is:"+requesttype);
            
            System.out.println("creation  of request is over");
            return request;
            } 
            else {
                // No new request to submit
                System.out.println("no new request to send");
                return null;
                
            }
        }
        
        @Override
        public void handleResponse(final HttpResponse response, final HttpContext context) throws IOException {
        	StatusLine st;
            if(requesttype.equals("heartbeat")) {
                    st = response.getStatusLine();
                    ct.setresponsedetails(st.getReasonPhrase(), ""+st.getProtocolVersion(), st.getStatusCode());
            }
            else  if(requesttype.equals("shutdown")){
                	st = response.getStatusLine();
                    ct.setresponsedetails(st.getReasonPhrase(), ""+st.getProtocolVersion(), st.getStatusCode());
            }
            
            context.setAttribute(RESPONSE_RECEIVED, Boolean.TRUE);
            
            // Signal completion of the request execution
            requestCount.countDown();
        }
        
    }
    
    static class MySessionRequestCallback implements SessionRequestCallback {

        private final CountDownLatch requestCount;        
        
        public MySessionRequestCallback(final CountDownLatch requestCount) {
            super();
            this.requestCount = requestCount;
        }
        
        @Override
        public void cancelled(final SessionRequest request) {
            System.out.println("Connect request cancelled: " + request.getRemoteAddress());
            this.requestCount.countDown();
        }

        @Override
        public void completed(final SessionRequest request) {
            System.out.println("Connect request completed: " + request.getRemoteAddress());
        }

        @Override
        public void failed(final SessionRequest request) {
            System.out.println("Connect request failed: " + request.getRemoteAddress());
            this.requestCount.countDown();
        }

        @Override
        public void timeout(final SessionRequest request) {
            System.out.println("Connect request timed out: " + request.getRemoteAddress());
            this.requestCount.countDown();
        }
        
    }
    
    public static class EventLogger implements EventListener {

        @Override
        public void connectionOpen(final NHttpConnection conn) {
            System.out.println("Connection open: " + conn);
            
        }

        @Override
        public void connectionTimeout(final NHttpConnection conn) {
            System.out.println("Connection timed out: " + conn);
        }

        @Override
        public void connectionClosed(final NHttpConnection conn) {
            System.out.println("Connection closed: " + conn);
        }

        @Override
        public void fatalIOException(final IOException ex, final NHttpConnection conn) {
            System.err.println("I/O error: " + ex.getMessage());
        }

        @Override
        public void fatalProtocolException(final HttpException ex, final NHttpConnection conn) {
            System.err.println("HTTP error: " + ex.getMessage());
        }
        
    }

}
